/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ejb.timeservice;

import com.core.EleitorDestino;
import com.core.GerenciadorFilaLocal;
import com.core.WebGatewaySMS;
import com.ejb.OperadorRemote;
import com.entidades.Aparelho;
import com.entidades.Campanha;
import com.entidades.Cliente;
import com.entidades.Pacote;
import com.entidades.dao.CampanhaDao;
import com.entidades.dao.DaoBasico;
import com.entidades.enumerandos.ProvedorEnum;
import com.entidades.enumerandos.StatusAparelhoEnum;
import com.entidades.enumerandos.StatusCampanhaEnum;
import com.entidades.enumerandos.StatusPacoteEnum;
import com.entidades.enumerandos.TipoAparelhoEnum;
import com.util.WorkerEJBUtil;
import excecoes.NenhumAparelhoDisponivelException;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.UserTransaction;

/**
 *
 * @author Ricardo
 */
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class EnviadorTSBean implements EnviadorTSRemote {

    @Resource
    private TimerService time;
    @Resource
    private UserTransaction utx;
    @PersistenceContext
    private EntityManager em;
    @EJB
    private GerenciadorFilaLocal fila;
    private OperadorRemote operario;
    private boolean ligado = false;
    private String nome;

    public void setNome(String nome) {
        this.nome = nome;
    }

    public boolean getStatus() {
        return isLigado();
    }

    public void incializaTimeService() {
        //começar daqui a 1 minuto e executar periodicamente a cada 1 minuto;
        //TODO validar se existem aparelhos com status conectado.

        if (!isLigado()) {

            //for(int x=1;x<fila.size();x++){
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            time.createTimer(0, 2000, null);
            //}
            System.out.println("ENVIADOR >>> Serviço iniciado");

        }
        setLigado(true);
    }

    public void paraServico() {
        setLigado(false);
    }

    @Timeout
    public void verificaTarefaEnvio(Timer timer) {

        System.out.println("ENVIADOR >>> Pesquisando por pacotes");
        Pacote pacote = fila.removePacoteParaEnvio();

        if (pacote != null) {
            System.out.println("ENVIADOR >>> Pacote encontrado para envio");
            ProvedorEnum provedorEscolhido = pacote.getCampanha().getProvedor();
            Cliente cliente = pacote.getCampanha().getUsuario().getCliente();
            if (provedorEscolhido.equals(ProvedorEnum.WEB)) {
                enviaByServicoWeb(pacote, cliente);
            }

            if (provedorEscolhido.equals(ProvedorEnum.GSM) || provedorEscolhido.equals(ProvedorEnum.IDEM)) {
                try {
                    this.enviaByAparelho(pacote, cliente, provedorEscolhido);
                } catch (NenhumAparelhoDisponivelException ex) {
                    Logger.getLogger(EnviadorTSBean.class.getName()).log(Level.SEVERE, null, ex);
                    pacote.setStatus(StatusPacoteEnum.PENDENTE); //StatusPacoteEnum.PENDENTE  ==> Erro de recurso
                    DaoBasico.merge(pacote, em, utx);
                }
            }
        }
    }

    private void enviaByServicoWeb(Pacote p, Cliente c) {
        String retorno = WebGatewaySMS.envia(p, c);

        if (retorno.equals("ok")) {
            p.setDataEnvio(new Date());
            p.setStatus(StatusPacoteEnum.ENVIADO);
            DaoBasico.merge(p, em, utx);


            c.setSaldoSms(c.getSaldoSms() - 1); //TODO checar se o saldo chego a 0;
            DaoBasico.merge(c, em, utx);


        } else {
            p.setStatus(StatusPacoteEnum.FALHA);
            DaoBasico.merge(c, em, utx);
        }
    }

    private void enviaByAparelho(Pacote p, Cliente c, ProvedorEnum provedor) throws NenhumAparelhoDisponivelException {

        Aparelho ap = null;

        if (provedor.equals(ProvedorEnum.GSM)) {
            ap = EleitorDestino.elege(p, em, p.getOperadora(), TipoAparelhoEnum.USB_MODEM, utx);
        }

        if (provedor.equals(ProvedorEnum.IDEM)) {
            ap = EleitorDestino.elege(p, em, p.getOperadora(), TipoAparelhoEnum.CELULAR, utx);
        }

        try {
            operario = WorkerEJBUtil.getOperadorBean(ap, em);
        } catch (NamingException ex) {
            p.setStatus(StatusPacoteEnum.PENDENTE); //StatusPacoteEnum.PENDENTE  ==> Erro de recurso
            DaoBasico.merge(p, em, utx);

            ap.setStatus(StatusAparelhoEnum.CONECTADO);
            DaoBasico.merge(ap, em, utx);

            Logger.getLogger(EnviadorTSBean.class.getName()).log(Level.SEVERE, null, ex);
        }


        String retorno = null;

        if (provedor.equals(ProvedorEnum.GSM)) {
            retorno = operario.enviar4GSM(p.getNumero(), p.getMensagem(), ap.getPorta(), "SMS");
        }

        if (provedor.equals(ProvedorEnum.IDEM)) {
            retorno = operario.enviar4Idem(p.getNumero(), p.getMensagem(), ap.getPorta(), "SMS");
        }

        System.out.println("########################################");
        System.out.println("##########");
        System.out.println("##########    RECURSO ELEITO");
        System.out.println("##########    Computador: " + ap.getComputador().getNome());
        System.out.println("##########    IP: " + ap.getComputador().getIp());
        System.out.println("##########    Porta: " + ap.getPorta());
        System.out.println("##########    Enviador: " + this.toString());
        System.out.println("##########");
        System.out.println("##########");
        System.out.println("##########    ENVIANDO SMS    ");
        System.out.println("##########    Destinatario: " + p.getNumero());
        System.out.println("##########    Mensagem: " + p.getMensagem());
        System.out.println("##########");
        System.out.println("##########    RETORNO DO ENVIO");
        System.out.println("##########    Retorno: " + retorno);
        System.out.println("##########");
        System.out.println("########################################");

        ap.setStatus(StatusAparelhoEnum.CONECTADO);
        ap.setDataHoraUltimoEnvio(new Date());




        if (retorno.equals(StatusPacoteEnum.ENVIADO.toString())) {
            p.setDataEnvio(new Date());
            p.setStatus(StatusPacoteEnum.ENVIADO);//StatusPacoteEnum.ENVIADO ==> Envio com Sucesso

            c.setSaldoSms(c.getSaldoSms() - 1); //TODO checar se o saldo chego a 0;

            ap.setTotalStatusENVIADO(ap.getTotalStatusENVIADO() + 1);
        }

        if (retorno.equals(StatusPacoteEnum.FALHA.toString())) {
            p.setStatus(StatusPacoteEnum.FALHA);//StatusPacoteEnum.FALHA ==> Erro no numero
            ap.setTotalStatusFALHA(ap.getTotalStatusFALHA() + 1);
        }

        if (retorno.equals(StatusPacoteEnum.PENDENTE.toString())) {
            p.setStatus(StatusPacoteEnum.PENDENTE);//StatusPacoteEnum.PENDENTE  ==> Erro de recurso
        }
        if (retorno.equals(StatusPacoteEnum.PENDENTE_NAO_EXISTE.toString())) {
            ap.setStatus(StatusAparelhoEnum.DESCONECTADO);
            ap.setInfo("Porta não existe ou não está acessível.");

            p.setStatus(StatusPacoteEnum.PENDENTE);//StatusPacoteEnum.PENDENTE  ==> Erro de recurso

            ap.setTotalStatusPENDENTE(ap.getTotalStatusPENDENTE() + 1);
        }
        if (retorno.equals(StatusPacoteEnum.PENDENTE_PORTA_EM_USO.toString())) {
            ap.setStatus(StatusAparelhoEnum.DESCONECTADO);
            ap.setInfo("Porta em uso por outro dispositivo.");

            p.setStatus(StatusPacoteEnum.PENDENTE);//StatusPacoteEnum.PENDENTE  ==> Erro de recurso

            ap.setTotalStatusPENDENTE(ap.getTotalStatusPENDENTE() + 1);
        }

        CampanhaDao campanhaDao = new CampanhaDao();
        campanhaDao.atualizarTotaisDeEnvio(p.getCampanha(), retorno, em, utx);
        
        DaoBasico.merge(p, em, utx);
        DaoBasico.merge(c, em, utx);
        DaoBasico.merge(ap, em, utx);
    }

    private StatusCampanhaEnum verificaStatusCampanha(Campanha campanha) {
        /*
         * Deverá ser respeitada a ordem de precedencia nesta validaçao, pois:
         * - Um pacote PENDENTE faz com que a campanha volte a ser enviada
         * - Nenhum pacote PENDENTE e um pacote com FALHA nao volta a ser enviado, porem marca a campanha como uma campanha falha
         * - Nenhum  pacote PENDENTE e nenhum com FALHA mostram que a campanha foi enviada com completo sucesso
         */
        List<Pacote> listaPacotesFalhos = findPacotesFalhos(campanha);
        if (listaPacotesFalhos != null && listaPacotesFalhos.size() > 0) {
            return StatusCampanhaEnum.FALHA;
        }
        List<Pacote> listaPacotesPendentes = findPacotesPendentes(campanha);
        if (listaPacotesPendentes != null && listaPacotesPendentes.size() > 0) {
            return StatusCampanhaEnum.PENDENTE;
        }
        return StatusCampanhaEnum.CONLUIDA;
    }

    private List<Pacote> findPacotesFalhos(Campanha c) {
        Query query = em.createQuery("select p from Pacote as p " +
                "where p.campanha=:c1 and " +
                "p.status =:st").setParameter("c1", c).setParameter("st", StatusPacoteEnum.FALHA);
        List<Pacote> listaRetorno = query.getResultList();
        return listaRetorno;
    }

    private List<Pacote> findPacotesPendentes(Campanha c) {
        Query query = em.createQuery("select p from Pacote as p " +
                "where p.campanha=:c1 and " +
                "p.status =:st").setParameter("c1", c).setParameter("st", StatusPacoteEnum.PENDENTE);
        List<Pacote> listaRetorno = query.getResultList();
        return listaRetorno;
    }

    /**
     * @return the ligado
     */
    public boolean isLigado() {
        return ligado;
    }

    /**
     * @param ligado the ligado to set
     */
    public void setLigado(boolean ligado) {
        this.ligado = ligado;
    }
}
